home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / C / Applications / Tcl-Tk 8.0 / Pre-installed version / tcl8.0 / unix / tclUnixPipe.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-08-15  |  29.1 KB  |  1,150 lines  |  [TEXT/CWIE]

  1. /* 
  2.  * tclUnixPipe.c --
  3.  *
  4.  *    This file implements the UNIX-specific exec pipeline functions,
  5.  *    the "pipe" channel driver, and the "pid" Tcl command.
  6.  *
  7.  * Copyright (c) 1991-1994 The Regents of the University of California.
  8.  * Copyright (c) 1994-1997 Sun Microsystems, Inc.
  9.  *
  10.  * See the file "license.terms" for information on usage and redistribution
  11.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  12.  *
  13.  * SCCS: @(#) tclUnixPipe.c 1.36 97/05/14 13:24:24
  14.  */
  15.  
  16. #include "tclInt.h"
  17. #include "tclPort.h"
  18.  
  19. /*
  20.  * The following macros convert between TclFile's and fd's.  The conversion
  21.  * simple involves shifting fd's up by one to ensure that no valid fd is ever
  22.  * the same as NULL.
  23.  */
  24.  
  25. #define MakeFile(fd) ((TclFile)((fd)+1))
  26. #define GetFd(file) (((int)file)-1)
  27.  
  28. /*
  29.  * This structure describes per-instance state of a pipe based channel.
  30.  */
  31.  
  32. typedef struct PipeState {
  33.     Tcl_Channel channel;/* Channel associated with this file. */
  34.     TclFile inFile;    /* Output from pipe. */
  35.     TclFile outFile;    /* Input to pipe. */
  36.     TclFile errorFile;    /* Error output from pipe. */
  37.     int numPids;    /* How many processes are attached to this pipe? */
  38.     Tcl_Pid *pidPtr;    /* The process IDs themselves. Allocated by
  39.                          * the creator of the pipe. */
  40.     int isNonBlocking;    /* Nonzero when the pipe is in nonblocking mode.
  41.                          * Used to decide whether to wait for the children
  42.                          * at close time. */
  43. } PipeState;
  44.  
  45. /*
  46.  * Declarations for local procedures defined in this file:
  47.  */
  48.  
  49. static int    PipeBlockModeProc _ANSI_ARGS_((ClientData instanceData,
  50.             int mode));
  51. static int    PipeCloseProc _ANSI_ARGS_((ClientData instanceData,
  52.             Tcl_Interp *interp));
  53. static int    PipeGetHandleProc _ANSI_ARGS_((ClientData instanceData,
  54.             int direction, ClientData *handlePtr));
  55. static int    PipeInputProc _ANSI_ARGS_((ClientData instanceData,
  56.             char *buf, int toRead, int *errorCode));
  57. static int    PipeOutputProc _ANSI_ARGS_((
  58.             ClientData instanceData, char *buf, int toWrite,
  59.             int *errorCode));
  60. static void    PipeWatchProc _ANSI_ARGS_((ClientData instanceData, int mask));
  61. static void    RestoreSignals _ANSI_ARGS_((void));
  62. static int    SetupStdFile _ANSI_ARGS_((TclFile file, int type));
  63.  
  64. /*
  65.  * This structure describes the channel type structure for command pipe
  66.  * based IO:
  67.  */
  68.  
  69. static Tcl_ChannelType pipeChannelType = {
  70.     "pipe",                /* Type name. */
  71.     PipeBlockModeProc,            /* Set blocking/nonblocking mode.*/
  72.     PipeCloseProc,            /* Close proc. */
  73.     PipeInputProc,            /* Input proc. */
  74.     PipeOutputProc,            /* Output proc. */
  75.     NULL,                /* Seek proc. */
  76.     NULL,                /* Set option proc. */
  77.     NULL,                /* Get option proc. */
  78.     PipeWatchProc,            /* Initialize notifier. */
  79.     PipeGetHandleProc,            /* Get OS handles out of channel. */
  80. };
  81.  
  82. /*
  83.  *----------------------------------------------------------------------
  84.  *
  85.  * TclpMakeFile --
  86.  *
  87.  *    Make a TclFile from a channel.
  88.  *
  89.  * Results:
  90.  *    Returns a new TclFile or NULL on failure.
  91.  *
  92.  * Side effects:
  93.  *    None.
  94.  *
  95.  *----------------------------------------------------------------------
  96.  */
  97.  
  98. TclFile
  99. TclpMakeFile(channel, direction)
  100.     Tcl_Channel channel;    /* Channel to get file from. */
  101.     int direction;        /* Either TCL_READABLE or TCL_WRITABLE. */
  102. {
  103.     int fd;
  104.  
  105.     if (Tcl_GetChannelHandle(channel, direction, (ClientData *) &fd)
  106.         == TCL_OK) {
  107.     return MakeFile(fd);
  108.     } else {
  109.     return (TclFile) NULL;
  110.     }
  111. }
  112.  
  113. /*
  114.  *----------------------------------------------------------------------
  115.  *
  116.  * TclpOpenFile --
  117.  *
  118.  *    Open a file for use in a pipeline.  
  119.  *
  120.  * Results:
  121.  *    Returns a new TclFile handle or NULL on failure.
  122.  *
  123.  * Side effects:
  124.  *    May cause a file to be created on the file system.
  125.  *
  126.  *----------------------------------------------------------------------
  127.  */
  128.  
  129. TclFile
  130. TclpOpenFile(fname, mode)
  131.     char *fname;            /* The name of the file to open. */
  132.     int mode;                /* In what mode to open the file? */
  133. {
  134.     int fd;
  135.  
  136.     fd = open(fname, mode, 0666);
  137.     if (fd != -1) {
  138.         fcntl(fd, F_SETFD, FD_CLOEXEC);
  139.  
  140.     /*
  141.      * If the file is being opened for writing, seek to the end
  142.      * so we can append to any data already in the file.
  143.      */
  144.  
  145.     if (mode & O_WRONLY) {
  146.         lseek(fd, 0, SEEK_END);
  147.     }
  148.  
  149.     /*
  150.      * Increment the fd so it can't be 0, which would conflict with
  151.      * the NULL return for errors.
  152.      */
  153.  
  154.     return MakeFile(fd);
  155.     }
  156.     return NULL;
  157. }
  158.  
  159. /*
  160.  *----------------------------------------------------------------------
  161.  *
  162.  * TclpCreateTempFile --
  163.  *
  164.  *    This function creates a temporary file initialized with an
  165.  *    optional string, and returns a file handle with the file pointer
  166.  *    at the beginning of the file.
  167.  *
  168.  * Results:
  169.  *    A handle to a file.
  170.  *
  171.  * Side effects:
  172.  *    None.
  173.  *
  174.  *----------------------------------------------------------------------
  175.  */
  176.  
  177. TclFile
  178. TclpCreateTempFile(contents, namePtr)
  179.     char *contents;        /* String to write into temp file, or NULL. */
  180.     Tcl_DString *namePtr;    /* If non-NULL, pointer to initialized 
  181.                  * DString that is filled with the name of 
  182.                  * the temp file that was created. */
  183. {
  184.     char fileName[L_tmpnam];
  185.     TclFile file;
  186.     size_t length = (contents == NULL) ? 0 : strlen(contents);
  187.  
  188.     tmpnam(fileName);
  189.     file = TclpOpenFile(fileName, O_RDWR|O_CREAT|O_TRUNC);
  190.     unlink(fileName);
  191.  
  192.     if ((file != NULL) && (length > 0)) {
  193.     int fd = GetFd(file);
  194.     while (1) {
  195.         if (write(fd, contents, length) != -1) {
  196.         break;
  197.         } else if (errno != EINTR) {
  198.         close(fd);
  199.         return NULL;
  200.         }
  201.     }
  202.     lseek(fd, 0, SEEK_SET);
  203.     }
  204.     if (namePtr != NULL) {
  205.     Tcl_DStringAppend(namePtr, fileName, -1);
  206.     }
  207.     return file;
  208. }
  209.  
  210. /*
  211.  *----------------------------------------------------------------------
  212.  *
  213.  * TclpCreatePipe --
  214.  *
  215.  *      Creates a pipe - simply calls the pipe() function.
  216.  *
  217.  * Results:
  218.  *      Returns 1 on success, 0 on failure. 
  219.  *
  220.  * Side effects:
  221.  *      Creates a pipe.
  222.  *
  223.  *----------------------------------------------------------------------
  224.  */
  225.  
  226. int
  227. TclpCreatePipe(readPipe, writePipe)
  228.     TclFile *readPipe;        /* Location to store file handle for
  229.                  * read side of pipe. */
  230.     TclFile *writePipe;        /* Location to store file handle for
  231.                  * write side of pipe. */
  232. {
  233.     int pipeIds[2];
  234.  
  235.     if (pipe(pipeIds) != 0) {
  236.     return 0;
  237.     }
  238.  
  239.     fcntl(pipeIds[0], F_SETFD, FD_CLOEXEC);
  240.     fcntl(pipeIds[1], F_SETFD, FD_CLOEXEC);
  241.  
  242.     *readPipe = MakeFile(pipeIds[0]);
  243.     *writePipe = MakeFile(pipeIds[1]);
  244.     return 1;
  245. }
  246.  
  247. /*
  248.  *----------------------------------------------------------------------
  249.  *
  250.  * TclpCloseFile --
  251.  *
  252.  *    Implements a mechanism to close a UNIX file.
  253.  *
  254.  * Results:
  255.  *    Returns 0 on success, or -1 on error, setting errno.
  256.  *
  257.  * Side effects:
  258.  *    The file is closed.
  259.  *
  260.  *----------------------------------------------------------------------
  261.  */
  262.  
  263. int
  264. TclpCloseFile(file)
  265.     TclFile file;    /* The file to close. */
  266. {
  267.     int fd = GetFd(file);
  268.  
  269.     /*
  270.      * Refuse to close the fds for stdin, stdout and stderr.
  271.      */
  272.     
  273.     if ((fd == 0) || (fd == 1) || (fd == 2)) {
  274.         return 0;
  275.     }
  276.     
  277.     Tcl_DeleteFileHandler(fd);
  278.     return close(fd);
  279. }
  280.  
  281. /*
  282.  *----------------------------------------------------------------------
  283.  *
  284.  * TclpCreateProcess --
  285.  *
  286.  *    Create a child process that has the specified files as its 
  287.  *    standard input, output, and error.  The child process runs
  288.  *    asynchronously and runs with the same environment variables
  289.  *    as the creating process.
  290.  *
  291.  *    The path is searched to find the specified executable.  
  292.  *
  293.  * Results:
  294.  *    The return value is TCL_ERROR and an error message is left in
  295.  *    interp->result if there was a problem creating the child 
  296.  *    process.  Otherwise, the return value is TCL_OK and *pidPtr is
  297.  *    filled with the process id of the child process.
  298.  * 
  299.  * Side effects:
  300.  *    A process is created.
  301.  *    
  302.  *----------------------------------------------------------------------
  303.  */
  304.  
  305.     /* ARGSUSED */
  306. int
  307. TclpCreateProcess(interp, argc, argv, inputFile, outputFile, errorFile, 
  308.     pidPtr)
  309.     Tcl_Interp *interp;        /* Interpreter in which to leave errors that
  310.                  * occurred when creating the child process.
  311.                  * Error messages from the child process
  312.                  * itself are sent to errorFile. */
  313.     int argc;            /* Number of arguments in following array. */
  314.     char **argv;        /* Array of argument strings.  argv[0]
  315.                  * contains the name of the executable
  316.                  * converted to native format (using the
  317.                  * Tcl_TranslateFileName call).  Additional
  318.                  * arguments have not been converted. */
  319.     TclFile inputFile;        /* If non-NULL, gives the file to use as
  320.                  * input for the child process.  If inputFile
  321.                  * file is not readable or is NULL, the child
  322.                  * will receive no standard input. */
  323.     TclFile outputFile;        /* If non-NULL, gives the file that
  324.                  * receives output from the child process.  If
  325.                  * outputFile file is not writeable or is
  326.                  * NULL, output from the child will be
  327.                  * discarded. */
  328.     TclFile errorFile;        /* If non-NULL, gives the file that
  329.                  * receives errors from the child process.  If
  330.                  * errorFile file is not writeable or is NULL,
  331.                  * errors from the child will be discarded.
  332.                  * errorFile may be the same as outputFile. */
  333.     Tcl_Pid *pidPtr;        /* If this procedure is successful, pidPtr
  334.                  * is filled with the process id of the child
  335.                  * process. */
  336. {
  337.     TclFile errPipeIn, errPipeOut;
  338.     int joinThisError, count, status, fd;
  339.     char errSpace[200];
  340.     int pid;
  341.     
  342.     errPipeIn = NULL;
  343.     errPipeOut = NULL;
  344.     pid = -1;
  345.  
  346.     /*
  347.      * Create a pipe that the child can use to return error
  348.      * information if anything goes wrong.
  349.      */
  350.  
  351.     if (TclpCreatePipe(&errPipeIn, &errPipeOut) == 0) {
  352.     Tcl_AppendResult(interp, "couldn't create pipe: ",
  353.         Tcl_PosixError(interp), (char *) NULL);
  354.     goto error;
  355.     }
  356.  
  357.     joinThisError = (errorFile == outputFile);
  358.     pid = vfork();
  359.     if (pid == 0) {
  360.     fd = GetFd(errPipeOut);
  361.  
  362.     /*
  363.      * Set up stdio file handles for the child process.
  364.      */
  365.  
  366.     if (!SetupStdFile(inputFile, TCL_STDIN)
  367.         || !SetupStdFile(outputFile, TCL_STDOUT)
  368.         || (!joinThisError && !SetupStdFile(errorFile, TCL_STDERR))
  369.         || (joinThisError &&
  370.             ((dup2(1,2) == -1) ||
  371.              (fcntl(2, F_SETFD, 0) != 0)))) {
  372.         sprintf(errSpace,
  373.             "%dforked process couldn't set up input/output: ",
  374.             errno);
  375.         write(fd, errSpace, (size_t) strlen(errSpace));
  376.         _exit(1);
  377.     }
  378.  
  379.     /*
  380.      * Close the input side of the error pipe.
  381.      */
  382.  
  383.     RestoreSignals();
  384.     execvp(argv[0], &argv[0]);
  385.     sprintf(errSpace, "%dcouldn't execute \"%.150s\": ", errno,
  386.         argv[0]);
  387.     write(fd, errSpace, (size_t) strlen(errSpace));
  388.     _exit(1);
  389.     }
  390.     if (pid == -1) {
  391.     Tcl_AppendResult(interp, "couldn't fork child process: ",
  392.         Tcl_PosixError(interp), (char *) NULL);
  393.     goto error;
  394.     }
  395.  
  396.     /*
  397.      * Read back from the error pipe to see if the child started
  398.      * up OK.  The info in the pipe (if any) consists of a decimal
  399.      * errno value followed by an error message.
  400.      */
  401.  
  402.     TclpCloseFile(errPipeOut);
  403.     errPipeOut = NULL;
  404.  
  405.     fd = GetFd(errPipeIn);
  406.     count = read(fd, errSpace, (size_t) (sizeof(errSpace) - 1));
  407.     if (count > 0) {
  408.     char *end;
  409.     errSpace[count] = 0;
  410.     errno = strtol(errSpace, &end, 10);
  411.     Tcl_AppendResult(interp, end, Tcl_PosixError(interp),
  412.         (char *) NULL);
  413.     goto error;
  414.     }
  415.     
  416.     TclpCloseFile(errPipeIn);
  417.     *pidPtr = (Tcl_Pid) pid;
  418.     return TCL_OK;
  419.  
  420.     error:
  421.     if (pid != -1) {
  422.     /*
  423.      * Reap the child process now if an error occurred during its
  424.      * startup.
  425.      */
  426.  
  427.     Tcl_WaitPid((Tcl_Pid) pid, &status, WNOHANG);
  428.     }
  429.     
  430.     if (errPipeIn) {
  431.     TclpCloseFile(errPipeIn);
  432.     }
  433.     if (errPipeOut) {
  434.     TclpCloseFile(errPipeOut);
  435.     }
  436.     return TCL_ERROR;
  437. }
  438.  
  439. /*
  440.  *----------------------------------------------------------------------
  441.  *
  442.  * RestoreSignals --
  443.  *
  444.  *      This procedure is invoked in a forked child process just before
  445.  *      exec-ing a new program to restore all signals to their default
  446.  *      settings.
  447.  *
  448.  * Results:
  449.  *      None.
  450.  *
  451.  * Side effects:
  452.  *      Signal settings get changed.
  453.  *
  454.  *----------------------------------------------------------------------
  455.  */
  456.  
  457. static void
  458. RestoreSignals()
  459. {
  460. #ifdef SIGABRT
  461.     signal(SIGABRT, SIG_DFL);
  462. #endif
  463. #ifdef SIGALRM
  464.     signal(SIGALRM, SIG_DFL);
  465. #endif
  466. #ifdef SIGFPE
  467.     signal(SIGFPE, SIG_DFL);
  468. #endif
  469. #ifdef SIGHUP
  470.     signal(SIGHUP, SIG_DFL);
  471. #endif
  472. #ifdef SIGILL
  473.     signal(SIGILL, SIG_DFL);
  474. #endif
  475. #ifdef SIGINT
  476.     signal(SIGINT, SIG_DFL);
  477. #endif
  478. #ifdef SIGPIPE
  479.     signal(SIGPIPE, SIG_DFL);
  480. #endif
  481. #ifdef SIGQUIT
  482.     signal(SIGQUIT, SIG_DFL);
  483. #endif
  484. #ifdef SIGSEGV
  485.     signal(SIGSEGV, SIG_DFL);
  486. #endif
  487. #ifdef SIGTERM
  488.     signal(SIGTERM, SIG_DFL);
  489. #endif
  490. #ifdef SIGUSR1
  491.     signal(SIGUSR1, SIG_DFL);
  492. #endif
  493. #ifdef SIGUSR2
  494.     signal(SIGUSR2, SIG_DFL);
  495. #endif
  496. #ifdef SIGCHLD
  497.     signal(SIGCHLD, SIG_DFL);
  498. #endif
  499. #ifdef SIGCONT
  500.     signal(SIGCONT, SIG_DFL);
  501. #endif
  502. #ifdef SIGTSTP
  503.     signal(SIGTSTP, SIG_DFL);
  504. #endif
  505. #ifdef SIGTTIN
  506.     signal(SIGTTIN, SIG_DFL);
  507. #endif
  508. #ifdef SIGTTOU
  509.     signal(SIGTTOU, SIG_DFL);
  510. #endif
  511. }
  512.  
  513. /*
  514.  *----------------------------------------------------------------------
  515.  *
  516.  * SetupStdFile --
  517.  *
  518.  *    Set up stdio file handles for the child process, using the
  519.  *    current standard channels if no other files are specified.
  520.  *    If no standard channel is defined, or if no file is associated
  521.  *    with the channel, then the corresponding standard fd is closed.
  522.  *
  523.  * Results:
  524.  *    Returns 1 on success, or 0 on failure.
  525.  *
  526.  * Side effects:
  527.  *    Replaces stdio fds.
  528.  *
  529.  *----------------------------------------------------------------------
  530.  */
  531.  
  532. static int
  533. SetupStdFile(file, type)
  534.     TclFile file;        /* File to dup, or NULL. */
  535.     int type;            /* One of TCL_STDIN, TCL_STDOUT, TCL_STDERR */
  536. {
  537.     Tcl_Channel channel;
  538.     int fd;
  539.     int targetFd = 0;        /* Initializations here needed only to */
  540.     int direction = 0;        /* prevent warnings about using uninitialized
  541.                  * variables. */
  542.  
  543.     switch (type) {
  544.     case TCL_STDIN:
  545.         targetFd = 0;
  546.         direction = TCL_READABLE;
  547.         break;
  548.     case TCL_STDOUT:
  549.         targetFd = 1;
  550.         direction = TCL_WRITABLE;
  551.         break;
  552.     case TCL_STDERR:
  553.         targetFd = 2;
  554.         direction = TCL_WRITABLE;
  555.         break;
  556.     }
  557.  
  558.     if (!file) {
  559.     channel = Tcl_GetStdChannel(type);
  560.     if (channel) {
  561.         file = TclpMakeFile(channel, direction);
  562.     }
  563.     }
  564.     if (file) {
  565.     fd = GetFd(file);
  566.     if (fd != targetFd) {
  567.         if (dup2(fd, targetFd) == -1) {
  568.         return 0;
  569.         }
  570.  
  571.             /*
  572.              * Must clear the close-on-exec flag for the target FD, since
  573.              * some systems (e.g. Ultrix) do not clear the CLOEXEC flag on
  574.              * the target FD.
  575.              */
  576.             
  577.             fcntl(targetFd, F_SETFD, 0);
  578.     } else {
  579.         int result;
  580.  
  581.         /*
  582.          * Since we aren't dup'ing the file, we need to explicitly clear
  583.          * the close-on-exec flag.
  584.          */
  585.  
  586.         result = fcntl(fd, F_SETFD, 0);
  587.     }
  588.     } else {
  589.     close(targetFd);
  590.     }
  591.     return 1;
  592. }
  593.  
  594. /*
  595.  *----------------------------------------------------------------------
  596.  *
  597.  * TclpCreateCommandChannel --
  598.  *
  599.  *    This function is called by the generic IO level to perform
  600.  *    the platform specific channel initialization for a command
  601.  *    channel.
  602.  *
  603.  * Results:
  604.  *    Returns a new channel or NULL on failure.
  605.  *
  606.  * Side effects:
  607.  *    Allocates a new channel.
  608.  *
  609.  *----------------------------------------------------------------------
  610.  */
  611.  
  612. Tcl_Channel
  613. TclpCreateCommandChannel(readFile, writeFile, errorFile, numPids, pidPtr)
  614.     TclFile readFile;        /* If non-null, gives the file for reading. */
  615.     TclFile writeFile;        /* If non-null, gives the file for writing. */
  616.     TclFile errorFile;        /* If non-null, gives the file where errors
  617.                  * can be read. */
  618.     int numPids;        /* The number of pids in the pid array. */
  619.     Tcl_Pid *pidPtr;        /* An array of process identifiers.
  620.                                  * Allocated by the caller, freed when
  621.                                  * the channel is closed or the processes
  622.                                  * are detached (in a background exec). */
  623. {
  624.     char channelName[20];
  625.     int channelId;
  626.     PipeState *statePtr = (PipeState *) ckalloc((unsigned) sizeof(PipeState));
  627.     int mode;
  628.  
  629.     statePtr->inFile = readFile;
  630.     statePtr->outFile = writeFile;
  631.     statePtr->errorFile = errorFile;
  632.     statePtr->numPids = numPids;
  633.     statePtr->pidPtr = pidPtr;
  634.     statePtr->isNonBlocking = 0;
  635.  
  636.     mode = 0;
  637.     if (readFile) {
  638.         mode |= TCL_READABLE;
  639.     }
  640.     if (writeFile) {
  641.         mode |= TCL_WRITABLE;
  642.     }
  643.     
  644.     /*
  645.      * Use one of the fds associated with the channel as the
  646.      * channel id.
  647.      */
  648.  
  649.     if (readFile) {
  650.     channelId = GetFd(readFile);
  651.     } else if (writeFile) {
  652.     channelId = GetFd(writeFile);
  653.     } else if (errorFile) {
  654.     channelId = GetFd(errorFile);
  655.     } else {
  656.     channelId = 0;
  657.     }
  658.  
  659.     /*
  660.      * For backward compatibility with previous versions of Tcl, we
  661.      * use "file%d" as the base name for pipes even though it would
  662.      * be more natural to use "pipe%d".
  663.      */
  664.  
  665.     sprintf(channelName, "file%d", channelId);
  666.     statePtr->channel = Tcl_CreateChannel(&pipeChannelType, channelName,
  667.             (ClientData) statePtr, mode);
  668.     return statePtr->channel;
  669. }
  670.  
  671. /*
  672.  *----------------------------------------------------------------------
  673.  *
  674.  * TclGetAndDetachPids --
  675.  *
  676.  *    This procedure is invoked in the generic implementation of a
  677.  *    background "exec" (An exec when invoked with a terminating "&")
  678.  *    to store a list of the PIDs for processes in a command pipeline
  679.  *    in interp->result and to detach the processes.
  680.  *
  681.  * Results:
  682.  *    None.
  683.  *
  684.  * Side effects:
  685.  *    Modifies interp->result. Detaches processes.
  686.  *
  687.  *----------------------------------------------------------------------
  688.  */
  689.  
  690. void
  691. TclGetAndDetachPids(interp, chan)
  692.     Tcl_Interp *interp;
  693.     Tcl_Channel chan;
  694. {
  695.     PipeState *pipePtr;
  696.     Tcl_ChannelType *chanTypePtr;
  697.     int i;
  698.     char buf[20];
  699.  
  700.     /*
  701.      * Punt if the channel is not a command channel.
  702.      */
  703.  
  704.     chanTypePtr = Tcl_GetChannelType(chan);
  705.     if (chanTypePtr != &pipeChannelType) {
  706.         return;
  707.     }
  708.  
  709.     pipePtr = (PipeState *) Tcl_GetChannelInstanceData(chan);
  710.     for (i = 0; i < pipePtr->numPids; i++) {
  711.         sprintf(buf, "%ld", TclpGetPid(pipePtr->pidPtr[i]));
  712.         Tcl_AppendElement(interp, buf);
  713.         Tcl_DetachPids(1, &(pipePtr->pidPtr[i]));
  714.     }
  715.     if (pipePtr->numPids > 0) {
  716.         ckfree((char *) pipePtr->pidPtr);
  717.         pipePtr->numPids = 0;
  718.     }
  719. }
  720.  
  721. /*
  722.  *----------------------------------------------------------------------
  723.  *
  724.  * PipeBlockModeProc --
  725.  *
  726.  *    Helper procedure to set blocking and nonblocking modes on a
  727.  *    pipe based channel. Invoked by generic IO level code.
  728.  *
  729.  * Results:
  730.  *    0 if successful, errno when failed.
  731.  *
  732.  * Side effects:
  733.  *    Sets the device into blocking or non-blocking mode.
  734.  *
  735.  *----------------------------------------------------------------------
  736.  */
  737.  
  738.     /* ARGSUSED */
  739. static int
  740. PipeBlockModeProc(instanceData, mode)
  741.     ClientData instanceData;        /* Pipe state. */
  742.     int mode;                /* The mode to set. Can be one of
  743.                                          * TCL_MODE_BLOCKING or
  744.                                          * TCL_MODE_NONBLOCKING. */
  745. {
  746.     PipeState *psPtr = (PipeState *) instanceData;
  747.     int curStatus;
  748.     int fd;
  749.  
  750. #ifndef    USE_FIONBIO    
  751.     if (psPtr->inFile) {
  752.         fd = GetFd(psPtr->inFile);
  753.         curStatus = fcntl(fd, F_GETFL);
  754.         if (mode == TCL_MODE_BLOCKING) {
  755.             curStatus &= (~(O_NONBLOCK));
  756.         } else {
  757.             curStatus |= O_NONBLOCK;
  758.         }
  759.         if (fcntl(fd, F_SETFL, curStatus) < 0) {
  760.             return errno;
  761.         }
  762.         curStatus = fcntl(fd, F_GETFL);
  763.     }
  764.     if (psPtr->outFile) {
  765.         fd = GetFd(psPtr->outFile);
  766.         curStatus = fcntl(fd, F_GETFL);
  767.         if (mode == TCL_MODE_BLOCKING) {
  768.             curStatus &= (~(O_NONBLOCK));
  769.         } else {
  770.             curStatus |= O_NONBLOCK;
  771.         }
  772.         if (fcntl(fd, F_SETFL, curStatus) < 0) {
  773.             return errno;
  774.         }
  775.     }
  776. #endif    /* !FIONBIO */
  777.  
  778. #ifdef    USE_FIONBIO
  779.     if (psPtr->inFile) {
  780.         fd = GetFd(psPtr->inFile);
  781.         if (mode == TCL_MODE_BLOCKING) {
  782.             curStatus = 0;
  783.         } else {
  784.             curStatus = 1;
  785.         }
  786.         if (ioctl(fd, (int) FIONBIO, &curStatus) < 0) {
  787.             return errno;
  788.         }
  789.     }
  790.     if (psPtr->outFile != NULL) {
  791.         fd = GetFd(psPtr->outFile);
  792.         if (mode == TCL_MODE_BLOCKING) {
  793.             curStatus = 0;
  794.         } else {
  795.             curStatus = 1;
  796.         }
  797.         if (ioctl(fd, (int) FIONBIO,  &curStatus) < 0) {
  798.             return errno;
  799.         }
  800.     }
  801. #endif    /* USE_FIONBIO */
  802.     
  803.     return 0;
  804. }
  805.  
  806. /*
  807.  *----------------------------------------------------------------------
  808.  *
  809.  * PipeCloseProc --
  810.  *
  811.  *    This procedure is invoked by the generic IO level to perform
  812.  *    channel-type-specific cleanup when a command pipeline channel
  813.  *    is closed.
  814.  *
  815.  * Results:
  816.  *    0 on success, errno otherwise.
  817.  *
  818.  * Side effects:
  819.  *    Closes the command pipeline channel.
  820.  *
  821.  *----------------------------------------------------------------------
  822.  */
  823.  
  824.     /* ARGSUSED */
  825. static int
  826. PipeCloseProc(instanceData, interp)
  827.     ClientData instanceData;    /* The pipe to close. */
  828.     Tcl_Interp *interp;        /* For error reporting. */
  829. {
  830.     PipeState *pipePtr;
  831.     Tcl_Channel errChan;
  832.     int errorCode, result;
  833.  
  834.     errorCode = 0;
  835.     result = 0;
  836.     pipePtr = (PipeState *) instanceData;
  837.     if (pipePtr->inFile) {
  838.     if (TclpCloseFile(pipePtr->inFile) < 0) {
  839.         errorCode = errno;
  840.     }
  841.     }
  842.     if (pipePtr->outFile) {
  843.     if ((TclpCloseFile(pipePtr->outFile) < 0) && (errorCode == 0)) {
  844.         errorCode = errno;
  845.     }
  846.     }
  847.  
  848.     if (pipePtr->isNonBlocking || TclInExit()) {
  849.     
  850.     /*
  851.          * If the channel is non-blocking or Tcl is being cleaned up, just
  852.          * detach the children PIDs, reap them (important if we are in a
  853.          * dynamic load module), and discard the errorFile.
  854.          */
  855.         
  856.         Tcl_DetachPids(pipePtr->numPids, pipePtr->pidPtr);
  857.         Tcl_ReapDetachedProcs();
  858.  
  859.         if (pipePtr->errorFile) {
  860.         TclpCloseFile(pipePtr->errorFile);
  861.         }
  862.     } else {
  863.         
  864.     /*
  865.          * Wrap the error file into a channel and give it to the cleanup
  866.          * routine.
  867.          */
  868.  
  869.         if (pipePtr->errorFile) {
  870.         errChan = Tcl_MakeFileChannel(
  871.         (ClientData) GetFd(pipePtr->errorFile), TCL_READABLE);
  872.         } else {
  873.             errChan = NULL;
  874.         }
  875.         result = TclCleanupChildren(interp, pipePtr->numPids, pipePtr->pidPtr,
  876.                 errChan);
  877.     }
  878.  
  879.     if (pipePtr->numPids != 0) {
  880.         ckfree((char *) pipePtr->pidPtr);
  881.     }
  882.     ckfree((char *) pipePtr);
  883.     if (errorCode == 0) {
  884.         return result;
  885.     }
  886.     return errorCode;
  887. }
  888.  
  889. /*
  890.  *----------------------------------------------------------------------
  891.  *
  892.  * PipeInputProc --
  893.  *
  894.  *    This procedure is invoked from the generic IO level to read
  895.  *    input from a command pipeline based channel.
  896.  *
  897.  * Results:
  898.  *    The number of bytes read is returned or -1 on error. An output
  899.  *    argument contains a POSIX error code if an error occurs, or zero.
  900.  *
  901.  * Side effects:
  902.  *    Reads input from the input device of the channel.
  903.  *
  904.  *----------------------------------------------------------------------
  905.  */
  906.  
  907. static int
  908. PipeInputProc(instanceData, buf, toRead, errorCodePtr)
  909.     ClientData instanceData;        /* Pipe state. */
  910.     char *buf;                /* Where to store data read. */
  911.     int toRead;                /* How much space is available
  912.                                          * in the buffer? */
  913.     int *errorCodePtr;            /* Where to store error code. */
  914. {
  915.     PipeState *psPtr = (PipeState *) instanceData;
  916.     int bytesRead;            /* How many bytes were actually
  917.                                          * read from the input device? */
  918.  
  919.     *errorCodePtr = 0;
  920.     
  921.     /*
  922.      * Assume there is always enough input available. This will block
  923.      * appropriately, and read will unblock as soon as a short read is
  924.      * possible, if the channel is in blocking mode. If the channel is
  925.      * nonblocking, the read will never block.
  926.      */
  927.  
  928.     bytesRead = read(GetFd(psPtr->inFile), buf, (size_t) toRead);
  929.     if (bytesRead > -1) {
  930.         return bytesRead;
  931.     }
  932.     *errorCodePtr = errno;
  933.     return -1;
  934. }
  935.  
  936. /*
  937.  *----------------------------------------------------------------------
  938.  *
  939.  * PipeOutputProc--
  940.  *
  941.  *    This procedure is invoked from the generic IO level to write
  942.  *    output to a command pipeline based channel.
  943.  *
  944.  * Results:
  945.  *    The number of bytes written is returned or -1 on error. An
  946.  *    output argument    contains a POSIX error code if an error occurred,
  947.  *    or zero.
  948.  *
  949.  * Side effects:
  950.  *    Writes output on the output device of the channel.
  951.  *
  952.  *----------------------------------------------------------------------
  953.  */
  954.  
  955. static int
  956. PipeOutputProc(instanceData, buf, toWrite, errorCodePtr)
  957.     ClientData instanceData;        /* Pipe state. */
  958.     char *buf;                /* The data buffer. */
  959.     int toWrite;            /* How many bytes to write? */
  960.     int *errorCodePtr;            /* Where to store error code. */
  961. {
  962.     PipeState *psPtr = (PipeState *) instanceData;
  963.     int written;
  964.  
  965.     *errorCodePtr = 0;
  966.     written = write(GetFd(psPtr->outFile), buf, (size_t) toWrite);
  967.     if (written > -1) {
  968.         return written;
  969.     }
  970.     *errorCodePtr = errno;
  971.     return -1;
  972. }
  973.  
  974. /*
  975.  *----------------------------------------------------------------------
  976.  *
  977.  * PipeWatchProc --
  978.  *
  979.  *    Initialize the notifier to watch the fds from this channel.
  980.  *
  981.  * Results:
  982.  *    None.
  983.  *
  984.  * Side effects:
  985.  *    Sets up the notifier so that a future event on the channel will
  986.  *    be seen by Tcl.
  987.  *
  988.  *----------------------------------------------------------------------
  989.  */
  990.  
  991. static void
  992. PipeWatchProc(instanceData, mask)
  993.     ClientData instanceData;        /* The pipe state. */
  994.     int mask;                /* Events of interest; an OR-ed
  995.                                          * combination of TCL_READABLE,
  996.                                          * TCL_WRITABEL and TCL_EXCEPTION. */
  997. {
  998.     PipeState *psPtr = (PipeState *) instanceData;
  999.     int newmask;
  1000.  
  1001.     if (psPtr->inFile) {
  1002.     newmask = mask & (TCL_READABLE | TCL_EXCEPTION);
  1003.     if (newmask) {
  1004.         Tcl_CreateFileHandler(GetFd(psPtr->inFile), mask,
  1005.             (Tcl_FileProc *) Tcl_NotifyChannel,
  1006.             (ClientData) psPtr->channel);
  1007.     } else {
  1008.         Tcl_DeleteFileHandler(GetFd(psPtr->inFile));
  1009.     }
  1010.     }
  1011.     if (psPtr->outFile) {
  1012.     newmask = mask & (TCL_WRITABLE | TCL_EXCEPTION);
  1013.     if (newmask) {
  1014.         Tcl_CreateFileHandler(GetFd(psPtr->outFile), mask,
  1015.             (Tcl_FileProc *) Tcl_NotifyChannel,
  1016.             (ClientData) psPtr->channel);
  1017.     } else {
  1018.         Tcl_DeleteFileHandler(GetFd(psPtr->outFile));
  1019.     }
  1020.     }
  1021. }
  1022.  
  1023. /*
  1024.  *----------------------------------------------------------------------
  1025.  *
  1026.  * PipeGetHandleProc --
  1027.  *
  1028.  *    Called from Tcl_GetChannelHandle to retrieve OS handles from
  1029.  *    inside a command pipeline based channel.
  1030.  *
  1031.  * Results:
  1032.  *    Returns TCL_OK with the fd in handlePtr, or TCL_ERROR if
  1033.  *    there is no handle for the specified direction. 
  1034.  *
  1035.  * Side effects:
  1036.  *    None.
  1037.  *
  1038.  *----------------------------------------------------------------------
  1039.  */
  1040.  
  1041. static int
  1042. PipeGetHandleProc(instanceData, direction, handlePtr)
  1043.     ClientData instanceData;    /* The pipe state. */
  1044.     int direction;        /* TCL_READABLE or TCL_WRITABLE */
  1045.     ClientData *handlePtr;    /* Where to store the handle.  */
  1046. {
  1047.     PipeState *psPtr = (PipeState *) instanceData;
  1048.  
  1049.     if (direction == TCL_READABLE && psPtr->inFile) {
  1050.     *handlePtr = (ClientData) GetFd(psPtr->inFile);
  1051.     return TCL_OK;
  1052.     }
  1053.     if (direction == TCL_WRITABLE && psPtr->outFile) {
  1054.     *handlePtr = (ClientData) GetFd(psPtr->outFile);
  1055.     return TCL_OK;
  1056.     }
  1057.     return TCL_ERROR;
  1058. }
  1059.  
  1060. /*
  1061.  *----------------------------------------------------------------------
  1062.  *
  1063.  * Tcl_WaitPid --
  1064.  *
  1065.  *    Implements the waitpid system call on Unix systems.
  1066.  *
  1067.  * Results:
  1068.  *    Result of calling waitpid.
  1069.  *
  1070.  * Side effects:
  1071.  *    Waits for a process to terminate.
  1072.  *
  1073.  *----------------------------------------------------------------------
  1074.  */
  1075.  
  1076. Tcl_Pid
  1077. Tcl_WaitPid(pid, statPtr, options)
  1078.     Tcl_Pid pid;
  1079.     int *statPtr;
  1080.     int options;
  1081. {
  1082.     int result;
  1083.     pid_t real_pid;
  1084.  
  1085.     real_pid = (pid_t) pid;
  1086.     while (1) {
  1087.     result = (int) waitpid(real_pid, statPtr, options);
  1088.     if ((result != -1) || (errno != EINTR)) {
  1089.         return (Tcl_Pid) result;
  1090.     }
  1091.     }
  1092. }
  1093.  
  1094. /*
  1095.  *----------------------------------------------------------------------
  1096.  *
  1097.  * Tcl_PidObjCmd --
  1098.  *
  1099.  *    This procedure is invoked to process the "pid" Tcl command.
  1100.  *    See the user documentation for details on what it does.
  1101.  *
  1102.  * Results:
  1103.  *    A standard Tcl result.
  1104.  *
  1105.  * Side effects:
  1106.  *    See the user documentation.
  1107.  *
  1108.  *----------------------------------------------------------------------
  1109.  */
  1110.  
  1111.     /* ARGSUSED */
  1112. int
  1113. Tcl_PidObjCmd(dummy, interp, objc, objv)
  1114.     ClientData dummy;        /* Not used. */
  1115.     Tcl_Interp *interp;        /* Current interpreter. */
  1116.     int objc;            /* Number of arguments. */
  1117.     Tcl_Obj *CONST *objv;    /* Argument strings. */
  1118. {
  1119.     Tcl_Channel chan;
  1120.     Tcl_ChannelType *chanTypePtr;
  1121.     PipeState *pipePtr;
  1122.     int i;
  1123.     Tcl_Obj *resultPtr, *longObjPtr;
  1124.  
  1125.     if (objc > 2) {
  1126.     Tcl_WrongNumArgs(interp, 1, objv, "?channelId?");
  1127.     return TCL_ERROR;
  1128.     }
  1129.     if (objc == 1) {
  1130.     Tcl_SetLongObj(Tcl_GetObjResult(interp), (long) getpid());
  1131.     } else {
  1132.         chan = Tcl_GetChannel(interp, Tcl_GetStringFromObj(objv[1], NULL),
  1133.         NULL);
  1134.         if (chan == (Tcl_Channel) NULL) {
  1135.         return TCL_ERROR;
  1136.     }
  1137.     chanTypePtr = Tcl_GetChannelType(chan);
  1138.     if (chanTypePtr != &pipeChannelType) {
  1139.         return TCL_OK;
  1140.     }
  1141.         pipePtr = (PipeState *) Tcl_GetChannelInstanceData(chan);
  1142.     resultPtr = Tcl_GetObjResult(interp);
  1143.         for (i = 0; i < pipePtr->numPids; i++) {
  1144.         longObjPtr = Tcl_NewLongObj((long) TclpGetPid(pipePtr->pidPtr[i]));
  1145.         Tcl_ListObjAppendElement(NULL, resultPtr, longObjPtr);
  1146.     }
  1147.     }
  1148.     return TCL_OK;
  1149. }
  1150.